<script setup lang="ts">
import {
  computed,
  onBeforeUnmount,
  onMounted,
  reactive,
  ref,
  watch,
} from "vue";
import { useRoute, useRouter } from "vue-router";

type Message = {
  id: number;
  role: "assistant" | "user";
  text: string;
  suggestion?: AssistantRoute;
};

type AssistantRoute = {
  path: string;
  label: string;
  description: string;
  searchableText: string;
};

const router = useRouter();
const route = useRoute();

const panelState = reactive({
  open: false,
  search: "",
  question: "",
  nextMessageId: 0,
});

const viewport = reactive({
  width: typeof window !== "undefined" ? window.innerWidth : 1280,
  height: typeof window !== "undefined" ? window.innerHeight : 720,
});

const fabPosition = reactive({
  x: viewport.width - 96,
  y: viewport.height - 120,
});

const dragState = reactive({
  active: false,
  offsetX: 0,
  offsetY: 0,
  moved: false,
  justDragged: false,
  startClientX: 0,
  startClientY: 0,
});

const baseMessages: Message[] = [
  {
    id: panelState.nextMessageId++,
    role: "assistant",
    text: "你好，我是智能客服。告诉我你想做什么，我来帮你定位页面。",
  },
];

const messages = ref<Message[]>(baseMessages);

const assistantDescriptions: Record<string, string> = {
  "/index": "进入主工作台与各类看板",
  "/indexeach": "查看单体看板及实时数据",
  "/BomList": "查看或搜索已有BOM结构",
  "/BomAdd": "录入新的BOM结构",
  "/IncomingInspectionList": "管理来料检验记录",
  "/IncomingInspectionAdd": "新增一条来料检验记录",
  "/ProductInspectionList": "查看制程/完工检验记录",
  "/ProductInspectionAdd": "填写新的制程/完工检验记录",
  "/DetectionPlanList": "查看检测计划与任务分配",
  "/DetectionPlanAdd": "配置一条新的检测计划",
  "/MaterialAdd": "新增物料主数据",
  "/Material.GetList": "查询物料主数据列表",
  "/Toolborrowheaderlist": "工具借用单据管理",
  "/Toolreturnorderlist": "工具归还单据管理",
  "/WorkTeamList": "查看所有班组编制情况",
};

const hiddenPaths = new Set(["/", "/home"]);

const splitCamelCase = (value: string) =>
  value.replace(/([a-z0-9])([A-Z])/g, "$1 $2");

const faqIntents = [
  {
    keywords: ["你好", "hello", "hi", "在吗"],
    answer: "你好！我一直在线，有什么可以帮你的吗？",
  },
  {
    keywords: ["帮助", "怎么用", "功能"],
    answer: "告诉我你想完成的任务，比如“新增来料检验”，我会帮你定位到对应页面。",
  },
  {
    keywords: ["谢谢", "感謝", "辛苦"],
    answer: "这是我应该做的，如需其他帮助随时叫我。",
  },
  {
    keywords: ["你是谁", "名字", "客服"],
    answer: "我是智能客服助手，负责帮你导航系统页面并回答常见问题。",
  },
];

const assistantRoutes = computed<AssistantRoute[]>(() => {
  const normalized: AssistantRoute[] = [];
  const seen = new Set<string>();

  router.getRoutes().forEach((record) => {
    if (!record.path || !record.path.startsWith("/") || hiddenPaths.has(record.path)) {
      return;
    }

    if (seen.has(record.path)) {
      return;
    }

    seen.add(record.path);

    const metaTitle = typeof record.meta?.title === "string" ? record.meta.title : undefined;
    const routeName = typeof record.name === "string" ? record.name : undefined;
    const labelSource = metaTitle ?? routeName ?? record.path.replace("/", "");
    const label = splitCamelCase(labelSource ?? "").trim() || record.path;
    const description =
      assistantDescriptions[record.path] ??
      (typeof record.meta?.description === "string"
        ? record.meta.description
        : "打开该页面");

    const keywords = [
      label,
      description,
      record.path,
      routeName ?? "",
      (record.meta?.keywords as string[] | undefined)?.join(" ") ?? "",
    ]
      .join(" ")
      .toLowerCase();

    normalized.push({
      path: record.path,
      label,
      description,
      searchableText: keywords,
    });
  });

  return normalized.sort((a, b) => a.label.localeCompare(b.label, "zh-Hans"));
});

const filteredRoutes = computed(() => {
  const keyword = panelState.search.trim().toLowerCase();
  if (!keyword) {
    return assistantRoutes.value.slice(0, 3);
  }

  return assistantRoutes.value
    .map((item) => ({
      item,
      score: scoreRoute(item, keyword),
    }))
    .filter(({ score }) => score > 0)
    .sort((a, b) => b.score - a.score)
    .slice(0, 3)
    .map(({ item }) => item);
});

function scoreRoute(item: AssistantRoute, keyword: string) {
  const tokens = keyword.split(/\s+/).filter(Boolean);
  if (!tokens.length) {
    return 0;
  }

  let score = 0;
  tokens.forEach((token) => {
    if (item.searchableText.includes(token)) {
      score += 2;
    }
    if (item.label.toLowerCase().includes(token)) {
      score += 1;
    }
  });

  const firstToken = tokens[0];
  if (firstToken && item.label.toLowerCase().startsWith(firstToken)) {
    score += 1;
  }

  return score;
}

function appendMessage(payload: Omit<Message, "id">) {
  messages.value.push({
    ...payload,
    id: panelState.nextMessageId++,
  });
}

function togglePanel() {
  panelState.open = !panelState.open;
  if (panelState.open) {
    setTimeout(() => {
      const input = document.querySelector<HTMLInputElement>("#assistant-search");
      input?.focus();
    }, 50);
  }
}

function closePanel() {
  panelState.open = false;
}

function handleNavigate(target: AssistantRoute) {
  router.push(target.path);
  appendMessage({
    role: "assistant",
    text: `已为你打开 ${target.label}。`,
  });
  closePanel();
}

function handleSearchSelect(target: AssistantRoute) {
  handleNavigate(target);
}

function findBestMatch(question: string) {
  const keyword = question.toLowerCase();
  let best: { route: AssistantRoute; score: number } | null = null;

  for (const item of assistantRoutes.value) {
    const score = scoreRoute(item, keyword) + (item.description.toLowerCase().includes(keyword) ? 1 : 0);
    if (!best || score > best.score) {
      best = { route: item, score };
    }
  }

  if (!best) {
    return null;
  }

  if (best.score > 0) {
    return best.route;
  }

  return null;
}

function detectIntent(question: string) {
  const lower = question.toLowerCase();
  return faqIntents.find((intent) =>
    intent.keywords.some((keyword) => lower.includes(keyword.toLowerCase()))
  );
}

function handleQuestion() {
  const question = panelState.question.trim();
  if (!question) {
    return;
  }

  appendMessage({ role: "user", text: question });

  const match = findBestMatch(question);
  if (match) {
    appendMessage({
      role: "assistant",
      text: `建议前往 ${match.label}，是否需要立即打开？`,
      suggestion: match,
    });
    panelState.question = "";
    return;
  }

  const intent = detectIntent(question);
  if (intent) {
    appendMessage({
      role: "assistant",
      text: intent.answer,
    });
    panelState.question = "";
    return;
  }

  appendMessage({
    role: "assistant",
    text: "暂时没有找到匹配的页面，请尝试换个描述或直接使用上方搜索。",
  });
  panelState.question = "";
}

function clamp(value: number, min: number, max: number) {
  return Math.max(min, Math.min(max, value));
}

function updateFabPosition(x: number, y: number) {
  fabPosition.x = clamp(x, 12, viewport.width - 76);
  fabPosition.y = clamp(y, 12, viewport.height - 76);
}

function handlePointerDown(event: PointerEvent) {
  dragState.active = true;
  dragState.offsetX = event.clientX - fabPosition.x;
  dragState.offsetY = event.clientY - fabPosition.y;
  dragState.moved = false;
  dragState.startClientX = event.clientX;
  dragState.startClientY = event.clientY;
  window.addEventListener("pointermove", handlePointerMove);
  window.addEventListener("pointerup", handlePointerUp);
  window.addEventListener("pointercancel", handlePointerUp);
}

function handlePointerMove(event: PointerEvent) {
  if (!dragState.active) {
    return;
  }
  const deltaX = event.clientX - dragState.startClientX;
  const deltaY = event.clientY - dragState.startClientY;
  const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
  const nextX = event.clientX - dragState.offsetX;
  const nextY = event.clientY - dragState.offsetY;
  updateFabPosition(nextX, nextY);
  if (distance > 4) {
    dragState.moved = true;
  }
}

function handlePointerUp() {
  if (dragState.moved) {
    dragState.justDragged = true;
  }
  dragState.active = false;
  dragState.moved = false;
  window.removeEventListener("pointermove", handlePointerMove);
  window.removeEventListener("pointerup", handlePointerUp);
  window.removeEventListener("pointercancel", handlePointerUp);
}

function handleFabClick() {
  if (dragState.justDragged) {
    dragState.justDragged = false;
    return;
  }
  togglePanel();
}

function handleResize() {
  viewport.width = window.innerWidth;
  viewport.height = window.innerHeight;
  updateFabPosition(fabPosition.x, fabPosition.y);
}

const fabStyle = computed(() => ({
  left: `${fabPosition.x}px`,
  top: `${fabPosition.y}px`,
}));

const panelStyle = computed(() => {
  const right = clamp(
    viewport.width - fabPosition.x - 64,
    16,
    Math.max(16, viewport.width - 360 - 16)
  );
  const bottom = clamp(
    viewport.height - fabPosition.y + 40,
    100,
    Math.max(100, viewport.height - 120)
  );
  return {
    right: `${right}px`,
    bottom: `${bottom}px`,
  };
});

onMounted(() => {
  window.addEventListener("resize", handleResize);
});

onBeforeUnmount(() => {
  window.removeEventListener("resize", handleResize);
  handlePointerUp();
});

watch(
  () => route.fullPath,
  () => {
    if (panelState.open) {
      appendMessage({
        role: "assistant",
        text: `你当前正处于 ${route.path} 页面，需要我带你去别的地方吗？`,
      });
    }
  }
);
</script>

<template>
  <div class="smart-assistant">
    <transition name="assistant-panel">
      <section
        v-if="panelState.open"
        class="assistant-panel"
        :style="panelStyle"
        @click.stop
      >
        <header class="assistant-header">
          <div>
            <p class="assistant-title">智能客服</p>
            <p class="assistant-subtitle">随时导航 / 问题咨询</p>
          </div>
          <button class="assistant-close" @click="closePanel" aria-label="关闭智能客服">
            ✕
          </button>
        </header>

        <div class="assistant-body">
          <label class="assistant-search">
            <input
              id="assistant-search"
              v-model="panelState.search"
              placeholder="例如：BOM、来料检验、工具借用..."
              type="search"
            />
          </label>

          <p class="assistant-section-title">快捷导航</p>
          <div class="assistant-routes">
            <button
              v-for="item in filteredRoutes"
              :key="item.path"
              class="assistant-route-card"
              type="button"
              @click="handleSearchSelect(item)"
            >
              <span class="route-label">{{ item.label }}</span>
              <span class="route-desc">{{ item.description }}</span>
            </button>
            <p v-if="!filteredRoutes.length" class="assistant-empty">
              暂无匹配，请尝试输入其他关键词。
            </p>
          </div>

          <p class="assistant-section-title">问一问</p>
          <div class="assistant-chat">
            <div class="assistant-chat-log">
              <div
                v-for="message in messages"
                :key="message.id"
                class="assistant-message"
                :data-role="message.role"
              >
                <p>{{ message.text }}</p>
                <button
                  v-if="message.suggestion"
                  type="button"
                  class="assistant-go"
                  @click="handleNavigate(message.suggestion)"
                >
                  立即前往
                </button>
              </div>
            </div>
            <form class="assistant-chat-input" @submit.prevent="handleQuestion">
              <input
                v-model="panelState.question"
                placeholder="描述你的需求，如：我要新增检验计划"
                autocomplete="off"
              />
              <button type="submit">发送</button>
            </form>
          </div>
        </div>
      </section>
    </transition>

    <button
      class="assistant-fab"
      type="button"
      :style="fabStyle"
      @click="handleFabClick"
      @pointerdown.prevent="handlePointerDown"
      aria-label="打开智能客服"
    >
      <svg
        class="assistant-fab-icon"
        viewBox="0 0 24 24"
        role="img"
        aria-hidden="true"
      >
        <path
          d="M5 3h14a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2h-3.2l-2.3 2.3a1 1 0 0 1-1.5-1.3l.1-.1V18H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2Z"
          fill="currentColor"
        />
        <circle cx="9" cy="9" r="1" fill="currentColor" />
        <circle cx="12" cy="9" r="1" fill="currentColor" />
        <circle cx="15" cy="9" r="1" fill="currentColor" />
      </svg>
    </button>
  </div>
</template>

<style scoped>
.smart-assistant {
  pointer-events: none;
}

.assistant-panel {
  position: fixed;
  width: 360px;
  max-height: calc(100vh - 160px);
  background: #ffffff;
  box-shadow: 0 20px 50px rgba(15, 55, 95, 0.16);
  border-radius: 20px;
  padding: 18px 20px 20px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  pointer-events: auto;
  z-index: 9999;
}

.assistant-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.assistant-title {
  margin: 0;
  font-size: 18px;
  font-weight: 600;
}

.assistant-subtitle {
  margin: 2px 0 0;
  font-size: 12px;
  color: #7a869a;
}

.assistant-close {
  border: none;
  background: #f5f5f5;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  cursor: pointer;
  font-size: 16px;
  line-height: 1;
  color: #5d6c80;
}

.assistant-body {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.assistant-search input {
  width: 100%;
  padding: 10px 14px;
  border-radius: 12px;
  border: 1px solid #d9e1ec;
  font-size: 14px;
}

.assistant-section-title {
  margin: 0;
  font-size: 13px;
  font-weight: 600;
  color: #4f5d75;
}

.assistant-routes {
  display: grid;
  gap: 10px;
}

.assistant-route-card {
  text-align: left;
  border: 1px solid #ebf0f7;
  border-radius: 14px;
  padding: 10px 12px;
  background: #f8fbff;
  cursor: pointer;
  transition: border-color 0.2s, transform 0.2s, background 0.2s;
  pointer-events: auto;
}

.assistant-route-card:hover {
  border-color: #6aa1ff;
  transform: translateY(-2px);
  background: #fff;
}

.route-label {
  display: block;
  font-size: 14px;
  font-weight: 600;
  color: #2f4368;
}

.route-desc {
  display: block;
  font-size: 12px;
  color: #7b87a2;
  margin-top: 2px;
}

.assistant-empty {
  margin: 0;
  font-size: 13px;
  color: #7a869a;
}

.assistant-chat {
  display: flex;
  flex-direction: column;
  gap: 8px;
  border: 1px solid #edf1f7;
  border-radius: 16px;
  padding: 10px;
  background: #fdfefe;
}

.assistant-chat-log {
  max-height: 150px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.assistant-message {
  padding: 8px 10px;
  border-radius: 12px;
  font-size: 13px;
  line-height: 1.4;
  background: #eff5ff;
  color: #1d2a44;
}

.assistant-message[data-role="user"] {
  background: #2f80ed;
  color: #fff;
  align-self: flex-end;
}

.assistant-go {
  margin-top: 6px;
  border: none;
  background: #4f8ef7;
  color: #fff;
  padding: 5px 10px;
  border-radius: 10px;
  cursor: pointer;
  font-size: 12px;
}

.assistant-chat-input {
  display: flex;
  gap: 8px;
}

.assistant-chat-input input {
  flex: 1;
  border-radius: 12px;
  border: 1px solid #dfe6f3;
  padding: 8px 12px;
  font-size: 13px;
}

.assistant-chat-input button {
  border: none;
  border-radius: 12px;
  background: #2f80ed;
  color: white;
  padding: 8px 16px;
  cursor: pointer;
  font-weight: 600;
}

.assistant-fab {
  position: fixed;
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background: linear-gradient(135deg, #2563eb, #7c3aed);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  box-shadow: 0 20px 35px rgba(51, 65, 85, 0.3);
  font-weight: 600;
  cursor: pointer;
  pointer-events: auto;
  z-index: 9998;
}

.assistant-fab-icon {
  width: 28px;
  height: 28px;
}

.assistant-panel-enter-active,
.assistant-panel-leave-active {
  transition: opacity 0.2s, transform 0.2s;
}

.assistant-panel-enter-from,
.assistant-panel-leave-to {
  opacity: 0;
  transform: translateY(10px);
}
</style>

